Proto commits in JonLatane/jonline

These commits are when the Protocol Buffers files have changed: (only the last 100 relevant commits are shown)

Commit:2452acb
Author:Jon Latane

Telnyx support

Commit:dcfe6f7
Author:Jon Latané
Committer:GitHub

More Market tweaks and Mastodon/Bluesky integration improvements (#115) * More Market tweaks and Mastodon/Bluesky integration improvements * fix tests

The documentation is generated from this commit.

Commit:fca94b5
Author:Jon Latane

More Market tweaks and Mastodon/Bluesky integration improvements

The documentation is generated from this commit.

Commit:70bd3ba
Author:Jon Latané
Committer:GitHub

Expand Market Features (#114)

Commit:d31e865
Author:Jon Latane

all the things

Commit:f2e8202
Author:Jon Latane

greatly expand market stuff

Commit:dd8d877
Author:Jon Latané
Committer:GitHub

Rellm's Market (#113)

Commit:f213741
Author:Jon Latane

Add PermissionsAccess purchase type, card details on payments/refunds, and Market SSR previews - protos/market.proto: PURCHASE_TYPE_PERMISSIONS_ACCESS lets an admin sell arbitrary Permission grants (e.g. pay-gating Facebook sync) as a subscription; MarketPaymentMethod/MarketRefundMethod now carry real card brand/last4/expiry resolved from Stripe at charge time - Backend: fulfillment additively grants the product's configured permissions to the buyer; stripe_sync resolves card details via PaymentIntent/PaymentMethod expansion; new logic::market_summary generates human-readable /market/product/:id SSR previews (og:description) wired into web::spa_pages, with an explanatory note on Rellm Hosting listings about full admin access (e.g. self-service Facebook sync pay-gating) - Elm: admin product form supports Permissions Access (comma-separated permission picker), and the Subscriptions section now shows billing history with card details when available Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:13e4ea8
Author:Jon Latane

Add Rellm's Market: Stripe-backed subscriptions for media storage, AI grants, and Rellm hosting - protos/market.proto: MarketProduct/MarketSubscription/MarketPurchase/MarketPayment/MarketRefund types and 5 new RPCs (GetMarketProducts, CreateMarketProduct, UpdateMarketProduct, GetMarketSubscriptions, MakeMarketPurchase) - Backend: DB schema, Stripe Checkout + webhook-driven fulfillment (never trusts the client-side redirect), single renew_market_subscriptions background job for recurring billing, StripeConfig in ServerConfiguration, and the long-overdue default_media_allocation_bytes wiring for new users - Elm: /market + /market/product/:id pages, IntegrationsTab Stripe editor, a read-only Subscriptions section on UserProfilePage, an AccountsPanel chip entry, and MARKET_TAB custom-nav wiring throughout Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:ea32126
Author:Jon Latane

pre-clauding

Commit:157ae81
Author:Jon Latané
Committer:GitHub

Video thumbnail image generation (#112)

Commit:926660c
Author:Jon Latane

stuff

Commit:96358eb
Author:Jon Latane

nav tab styles

Commit:5029c53
Author:Jon Latané
Committer:GitHub

Media overhaul for quota tracking + future enhancements (#110) * proto changes * Wire up Media storage quotas end-to-end Backs the media-overhaul proto scaffolding (User.media_storage_limit_bytes/ media_storage_bytes_used, Media/MediaReference.sizes) all the way through: - DB: restructure media's minio_path/content_type/converted_sizes/aspect_ratio into one `sizes` JSONB array (one entry per stored copy, original included), plus indexes for the new per-user lookups and the startup backfill query. - Backend: quota enforcement on upload (413 + message when it would exceed User.media_storage_limit_bytes), a denormalized media_storage_bytes_used counter (mirroring the existing follower_count/post_count pattern), and a non-blocking startup task that backfills real byte counts for pre-existing Media via MinIO stats. - New UpdateMedia/DeleteMediaSizes RPCs, with specs. - Elm: quota error surfacing + a used/available storage readout in MyMediaPanel; RellmAccount now tracks the two new User fields. - Tamagui: content-type/aspect-ratio call sites updated for the move from flat Media fields to per-size MediaSize entries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Admin-settable storage quotas on profiles; MediaViewerPanel editing - UpdateUser: admins can now set/clear any user's media_storage_limit_bytes (self-updates can't touch their own). Adds MediaReference.user_id so MediaViewerPanel can tell who owns a given item. - UserProfilePage: a new "Media Storage" section (collapsed, right above Permissions) lets an admin edit a user's quota (number + KB/MB/GB unit, or Unlimited), and shows the profile owner their own percent-used readout plus a "Show My Media" button opening MyMediaPanel. - MediaViewerPanel: an Edit button (owner-or-admin only) opens a small overlay to rename/redescribe the item and delete individual sizes (original/small/medium/large, each labeled with its friendly byte size), via the new UpdateMedia/DeleteMediaSizes RPCs. Its update now threads AccountsPanel.Model (for the RPC calls) and forwards an optional account-refresh, like MyMediaPanel already does; all 3 call sites in Shared.elm updated accordingly. - New Shared.ByteFormat module (human-readable formatting/parsing for byte counts) shared by MyMediaPanel, UserProfilePage, and MediaViewerPanel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Replace Media/MediaReference.user_id with a full Author authors.proto's Author.avatar already needed MediaReference, so importing authors.proto back into media.proto for Media/MediaReference.author would have been a circular file import (protoc rejects those even though the recursive types themselves are fine). Resolved by moving Author into media.proto itself and deleting authors.proto -- its other importers (ai_providers.proto, sync.proto, messages.proto, posts.proto) now just depend on media.proto instead, which most already did anyway. Media/MediaReference.author is populated with a real Author only at the media-specific RPCs (GetMedia, UpdateMedia, DeleteMediaSizes, GenerateMedia, via a batched author lookup); every other Media/MediaReference marshaling call site (User/Group avatars, Post media, ...) passes None, since the owner is already obvious from context there and this avoids needing an author lookup at every single one. Elm/Rust both represent the Author<->MediaReference recursion with a boxed/ wrapped indirection (prost's `boxed` field option resp. elm-protobuf's wrapper type + wrap/unwrap functions) since neither language allows an unboxed self-referential struct. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Switch Twilio auth from Auth Token to API Keys TwilioConfig previously authenticated with the account's own Auth Token (twilio_api_key, paired with the Account SID) -- a single unscoped, full-access credential with no way to revoke it without rotating everything else that uses it. Switched to a Twilio API Key instead: twilio_api_key_sid/twilio_api_key_secret now authenticate the request (Basic Auth), while twilio_account_sid is kept only for the API's URL path, never for authentication. Twilio's own docs recommend a Restricted API Key scoped to just /twilio/messaging/messages/create for this. Existing TwilioConfigs were stored under the old (now invalid) shape, so a data-only migration clears them -- admins need to re-enter credentials as an API Key pair via the (also newly fixed) Integrations tab. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Format verification SMS with server name and CDN frontend host The SMS body was a bare "Your verification code is {code}", giving a recipient no way to tell which server (of potentially several Rellm instances) is texting them. Now formats as "Phone verification requested from {server_info.name} ({external_cdn_config.frontend_host}). Your code is: {code}. Do not share this code with anyone." -- omitting the parenthetical when no CDN frontend host is configured. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Commit:eed1abc
Author:Jon Latané
Committer:GitHub

Rename AIModelProvider/AvailableAIModel/EventInstance for clarity (#109) * Rename AIModelProvider->AIProvider, AvailableAIModel->AIModel, EventInstance->Occasion Renames these types throughout the proto contracts, Rust backend (including DB tables/columns via new migrations), and Elm/Tamagui/Flutter frontends: - AIModelProvider -> AIProvider (ai_model_providers.proto -> ai_providers.proto, ai_model_providers/ai_model_provider_grants tables -> ai_providers/ai_provider_grants) - AvailableAIModel -> AIModel (proto/backend only; no DB table) - EventInstance -> Occasion (event_instances table -> occasions, plus event_instance_sync_destinations -> occasion_sync_destinations and all event_instance_id/event_instance_count columns -> occasion_id/occasion_count) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Clean up remaining Occasion-related instance/instances naming Extends the EventInstance->Occasion rename to every remaining Occasion-related use of "instance"/"instances" across protos, the Rust backend, and the Elm/Tamagui/Flutter frontends (the Event.instances proto field is now Event.occasions), while leaving genuinely unrelated uses of "instance" untouched (Mastodon server instances, Rocket/cluster server instances, the SyncDestinationStatus.destination_instance_id field, generic "for instance" prose, etc.). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix Integrations tab not showing saved Twilio/Bird settings twilio_config/bird_config/preferred_verification_apis are admin-only- serialized fields, stripped from the unauthenticated GetServerConfiguration probe RellmServers.configurationOf reflects. IntegrationsTab was reading that probe directly, so it always rendered blank/default settings regardless of what was actually saved -- a race condition already solved for cluster_resources in ClusterTab (its own authenticated GetServerConfiguration fetch, fired from every point connectivity could plausibly have settled). Ported that same pattern to IntegrationsTab. Also adds configure_server tests locking in that Twilio's Auth Token and Bird's Access Key are never returned to the client and are preserved (not clobbered) when a save leaves that field blank. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Commit:29383da
Author:Jon Latane

Clean up remaining Occasion-related instance/instances naming Extends the EventInstance->Occasion rename to every remaining Occasion-related use of "instance"/"instances" across protos, the Rust backend, and the Elm/Tamagui/Flutter frontends (the Event.instances proto field is now Event.occasions), while leaving genuinely unrelated uses of "instance" untouched (Mastodon server instances, Rocket/cluster server instances, the SyncDestinationStatus.destination_instance_id field, generic "for instance" prose, etc.). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:c7d335c
Author:Jon Latane

Rename AIModelProvider->AIProvider, AvailableAIModel->AIModel, EventInstance->Occasion Renames these types throughout the proto contracts, Rust backend (including DB tables/columns via new migrations), and Elm/Tamagui/Flutter frontends: - AIModelProvider -> AIProvider (ai_model_providers.proto -> ai_providers.proto, ai_model_providers/ai_model_provider_grants tables -> ai_providers/ai_provider_grants) - AvailableAIModel -> AIModel (proto/backend only; no DB table) - EventInstance -> Occasion (event_instances table -> occasions, plus event_instance_sync_destinations -> occasion_sync_destinations and all event_instance_id/event_instance_count columns -> occasion_id/occasion_count) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:49702a0
Author:Jon Latané
Committer:GitHub

RSS/Atom Support (#108)

Commit:ee9d91e
Author:Jon Latane

Add Twilio/Bird SMS verification for phone ContactMethods Adds StartContactMethodVerification/VerifyContactMethod RPCs (self-only, 6-digit code, 10-minute expiry, 5-attempt cap, 60s resend cooldown), wires phone/email into UpdateUser (previously never persisted), and supports two pluggable SMS providers (Twilio, and Bird as a cheaper alternative) with admin-configurable preference ordering. UserProfilePage gets a collapsible Contact Methods section (deep-linkable via #contact-methods) with the verify flow; ServerInformationPage gets an admin-only Integrations tab for provider credentials and preference. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:ae7b56c
Author:Jon Latane

more stuff

Commit:b115709
Author:Jon Latane

Document RSS/Atom SyncSources and the /rss.xml, /atom.xml endpoints README.md and rellm.proto's SyncSources doc were still describing the pre-RSS/Atom world (iCal as the only source type, Event.sync_source, EventInstance.sync_source_instance_id) -- brings both up to date and adds RSS/Atom subsections alongside the existing iCal one. Also documents the new GET /rss.xml and GET /atom.xml (?user_id=-scoped) endpoints next to /calendar.ics's own docs in rellm.proto. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:433e9ef
Author:Jon Latane

Add SYNC_POSTS_FROM_RSS/ATOM permissions, RSS/Atom feed output, and synced-post edit lock Permissions: SYNC_POSTS_FROM_RSS (701)/SYNC_POSTS_FROM_ATOM (702), gating create/update SyncSource per its configuration's own type (required_sync_source_permission) instead of the single SYNC_EVENTS_FROM_ICS every source used to share. UserProfilePage's type selector now only offers kinds the viewer actually holds permission for, and the Sync Sources section's add row is gated on holding at least one of the three. Posts.editContentButton now hides for a Post with syncSource set (RSS/Atom-derived), same "would just be clobbered on next sync" reasoning EventPage.hasIcsSyncSource already applies to synced Events -- title/link have no separate edit affordance on postDetail to lock. Adds backend/src/web/rss_subscription.rs and atom_subscription.rs (GET /rss.xml, GET /atom.xml, both ?user_id=-scoped) serving Rellm's own Posts out as feeds -- the reverse direction of feed_sync, which pulls external feeds in. Mirrors ical_subscription.rs's shape. PostsPage.elm gets an Export popover mirroring EventsPage's ICS one, with RSS and Atom links plus side-by-side copy buttons. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:5b58c92
Author:Jon Latane
Committer:Jon Latane

Move SyncSource identity onto Posts; add RSS/Atom feed sync Denormalizes sync_source_id/uid/recurrence_anchor from events/event_instances down onto posts (a true 1:1 extension-table relationship), so any Post can now carry a SyncSource, not just an Event/EventInstance. Replaces the old single unique index with two partial ones (recurring vs. non-recurring) since Postgres never treats two NULLs as colliding, which would otherwise silently stop protecting a plain synced Post or an Event's own series-level Post from duplication - exactly the class of bug the 2026-09-04 duplicate-events incident was about. Adds feed_sync.rs (RSS/Atom via feed-rs, which parses both into one shape) alongside the existing event_sync.rs (ICS), dispatched by SyncSource.configuration's variant. delete_sync_source simplifies to a single posts-table operation that now works uniformly across content types. Event.sync_source and EventInstance.sync_source_instance_id are dropped from the wire (reserved) in favor of Post.sync_source, which every Event/EventInstance already carries via its own Post. Adds a source-type selector and updates the synced-count button to show post counts for RSS/Atom sources in UserProfilePage.elm. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:10c1d4d
Author:Jon Latané
Committer:GitHub

Intra-cluster resource sharing (#107)

Commit:bae5b17
Author:Jon Latane

cluster resource locking, event timezones, a few small cross-protocol AccountsPanel UI tweaks

Commit:2b8b4b3
Author:Jon Latane

intra-cluster resource sharing

Commit:e26fa19
Author:Jon Latané
Committer:GitHub

Rename to Rellm (#104) * just update the first few paragraphs/link sections of README.md, so we have a tiny example * Rename Jonline to Rellm (Rust, Elm, LLM) Renames the project/protocol from Jonline to Rellm across the repo: proto package (protos/jonline.proto -> protos/rellm.proto), backend crate/binary, Elm/Tamagui/Flutter frontends (regenerated proto bindings, package names, source references), Docker image names, Homebrew/Linux launcher scripts (docs/homebrew_jonline.sh -> docs/rellm_homebrew.sh, linux_jonline.sh -> rellm_linux.sh), CI workflow, and k8s manifests/docs. The jonline.io domain itself is intentionally left untouched everywhere (it's a live hostname, not the project name) -- README now also lists a same-formatted Rellm.social entry alongside it. Includes deploys/rename_jonline_to_rellm.{sh,py}, the scripts used to do the bulk of this rename, kept for reference/future similar renames. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix remaining rename gaps: missed filenames and a live k8s namespace Ten hand-written source files (Flutter models, Tamagui about-page/service worker, and the elm-spa test file) had their *content* renamed to Rellm by the original rename pass but kept their old jonline_*/about_jonline filenames, silently breaking every by-path import into them (caught by elm-review's module-name check and tsc). Renamed the files to match. Also reverted the jonline.io deploy's Kubernetes *namespace* (`-n jonline`) in server_ci_cd.yml back from the blanket-renamed `-n rellm`: unlike the Deployment/StatefulSet/Service names inside it (safe to rename, since the Postgres/MinIO PVCs are referenced by static claimName, not derived from the StatefulSet name), Kubernetes has no rename-namespace operation -- actually renaming the namespace would mean recreating every resource in a new one, including the Postgres/MinIO data, for a purely internal id no user ever sees. Documented both fixes in the rename script for next time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix false-positive rename in backend/README.md "get.jonline"/"getjonline" is a display pun on the unrelated getj.online domain (a separate personal test deployment), not a reference to the Jonline protocol name -- the blanket rename script matched it by coincidental substring. Restored both to their original form. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Restore JONLINEIO cloudflare token * remove old load_balancer/Dockerfile --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Commit:a1a05aa
Author:Jon Latane

Rename Jonline to Rellm (Rust, Elm, LLM) Renames the project/protocol from Jonline to Rellm across the repo: proto package (protos/jonline.proto -> protos/rellm.proto), backend crate/binary, Elm/Tamagui/Flutter frontends (regenerated proto bindings, package names, source references), Docker image names, Homebrew/Linux launcher scripts (docs/homebrew_jonline.sh -> docs/rellm_homebrew.sh, linux_jonline.sh -> rellm_linux.sh), CI workflow, and k8s manifests/docs. The jonline.io domain itself is intentionally left untouched everywhere (it's a live hostname, not the project name) -- README now also lists a same-formatted Rellm.social entry alongside it. Includes deploys/rename_jonline_to_rellm.{sh,py}, the scripts used to do the bulk of this rename, kept for reference/future similar renames. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:e9def3d
Author:Jon Latané
Committer:GitHub

Add Cross-Protocol Federation (BlueSky/Mastodon) (#103) * Prep work for trans-protocol support * tests passing * Denormalize SyncSource matching onto event_instances with a DB unique constraint Replaces the app-level events.info->>'sync_source_uid' JSON-key lookup (the root cause of the 2026-09-04 duplicate-events incident, where a plain code rename silently orphaned every pre-existing synced Event) with real, indexed columns on event_instances: sync_source_id, sync_source_uid, and sync_source_recurrence_anchor (split out of the old sync_source_instance_id composite string -- the anchor is an occurrence's stable identity within its series, distinct from its own starts_at once a RECURRENCE-ID override moves it). A unique index on (sync_source_id, sync_source_uid, sync_source_recurrence_anchor) makes a duplicate occurrence a hard DB constraint violation instead of a silent duplicate insert. Also fixes DeleteSyncSource's "detach but keep events" path, which didn't know about event_instances' own new sync_source_id FK, and adds regression tests proving both the resync-is-idempotent behavior and the DB constraint itself reject the exact failure mode from the incident. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add client-side Mastodon account connection flow Lets a user connect a Mastodon account entirely from the browser, no backend involvement: `UI.mastodonServersStrip` shows each instance the current server's admin has configured (`FederationInfo.mastodonServers`), with an alert badge if it has no app ID yet and a "Connect" button otherwise. Clicking it drives a new "mastodon" provider on the existing `Ports.facebookLoginPopup` OAuth-popup mechanism: since Mastodon has no fixed app to register against up front, the popup opens blank synchronously (to dodge popup blockers), then dynamically self-registers a throwaway app (`POST /api/v1/apps`), builds a PKCE S256 challenge, and only then navigates to the instance's real authorize URL. The resulting code is exchanged for a token directly against the instance (no `client_secret` needed, since PKCE alone authenticates a fresh per-attempt public client) and the connected account is verified via `GET /api/v1/accounts/verify_credentials` before being added to `Shared.AccountsPanel`. Verified Mastodon's `/api/v1/apps` and `/oauth/token` are both CORS-open (like Bluesky's equivalent endpoints), so this whole flow needs zero Jonline backend support. Scope for this pass: connecting an account and capturing its token. Connected accounts are session-only (not yet persisted across reloads) and aren't yet wired into any post-fetching/translation layer -- both are natural follow-ups once there's an actual reason to keep them around. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix yarn tsc: preserve federationInfo.mastodonServers on server settings save server_details_screen.tsx built a bare federationInfo literal with just servers, which used to slide by since facebookAuthConfig/xTwitterAuthConfig are optional fields -- but mastodonServers is a repeated (required, not optional) field in the generated type, so it failed type-checking outright once that field existed. Fixes it properly rather than just satisfying the compiler: spreads the existing federationInfo first (so facebook/x-twitter config isn't silently dropped either) and preserves mastodonServers explicitly, only overriding servers. `make test` doesn't run `yarn tsc` (only `yarn test`/vitest), which is why this slipped through the previous push -- confirmed clean now with both. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add client-side Bluesky account connection flow Mirrors the Mastodon connect flow's end state (a connected account with a captured token, session-only, not yet wired into anything) but with a much simpler mechanism underneath: Bluesky's com.atproto.server.createSession takes a handle and App Password directly -- no OAuth popup, no app registration, no PKCE. Just a plain inline form (UI.blueskyConnectSection), posting straight to bsky.social and landing the resulting handle/accessJwt in Shared.AccountsPanel.blueskyAccounts. The session response itself already carries the handle, so there's no separate verify-credentials round trip the way Mastodon's OAuth code needed. Known first-pass limitation, called out in BlueskyAccount's own doc comment: always calls bsky.social directly rather than resolving a handle to its actual PDS first, so a self-hosted-PDS account won't connect yet. Covers the common case (accounts hosted on bsky.social by default). No proto or backend changes needed -- this is Elm-only, same as the Mastodon flow ended up being. Verified with `make test` (backend/elm-review/tamagui/flutter all green) and `yarn tsc` (not covered by `make test`, per the previous fix on this branch) before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add Mastodon/Bluesky post translation + bootstrap Elm tests Translation layer: Shared.Federation.Mastodon/Bluesky decode each protocol's own API shape (Mastodon's Status entity, a Bluesky feedViewPost) and translate it into a real Jonline Post -- namespaced ids ("mastodon:host:id" / "bluesky:at://...") so they can never collide with a real Jonline post's own id, GLOBALPUBLIC visibility (both sources are definitionally public), REPLY/POST context from each protocol's own reply signal, and avatars carried via MediaReference.url (added earlier this branch) rather than a Jonline media id, since they're not and never will be Jonline-hosted. Deliberately out of scope for this pass: actually fetching from a live page and merging into PostsPage's postsByServer. That page's fetch triggers are scattered across many call sites (tab changes, account toggles, new-post insertion, ...), and wiring a translated feed in safely deserves its own pass rather than a rushed edit riding on top of this one -- decoder/toPost are complete and fully tested on their own. Elm tests: these are the first in the app -- bootstrapped elm-test (elm-explorations/test, rtfeldman/elm-iso8601-date-strings promoted to a direct dependency) and wired it into `make test` via a new elm_test Makefile target. Added Support.MastodonFactory/BlueskyFactory (paired JSON-string + already-decoded fixtures built from one shared Overrides record, so decoder tests can check the two agree) and Support.PostFactory (a thin default-Post wrapper for the two fields sorting tests care about). JonlineTests covers the main protocol's own Components.Posts.postTimestamp -- including the actual point of this feature: a real Jonline post and translated Mastodon/Bluesky posts sorting correctly together, not just within their own protocol. Verified with `make test` (backend/elm-review/elm-test/tamagui/flutter all green) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Dedupe nonEmpty and Http error-resolver boilerplate - Shared.Federation.Common.nonEmpty replaces the identical copy in both Mastodon.elm and Bluesky.elm. - Shared.AccountsPanel.jsonResolver factors out the GoodStatus_/NetworkError_/Timeout_/BadUrl_ boilerplate that verifyMastodonCredentialsTask and createBlueskySessionTask had duplicated, parameterized by a decoder and a BadStatus_-specific error builder (bare status code for Mastodon, a parsed message for Bluesky) so each keeps its own error-shape handling. Verified with `make test` (all green) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Wire Mastodon/Bluesky translated posts into the live Posts feed Adds Mastodon.fetchPosts/Bluesky.fetchPosts (fetch + toPost, composed via the now-shared Shared.Federation.Common.jsonResolver) and calls them from Components.Pages.PostsPage.fetchFederatedPosts for every connected account, storing results in a new model.federatedPosts dict keyed by a synthetic host ("mastodon:instanceHost" / "bluesky:handle") that can never collide with a real AccountsPanel.Server.frontendHost. That synthetic-host trick is what makes the rest of this a small, additive change rather than a rewrite: postCardView already derives maybeServer/maybeAccount via AccountsPanel.serverForHost/ enabledAccountForServer, plain host-keyed lookups that already return Nothing for an unrecognized host -- so a federated post renders through the exact same Posts.postCard as everything else, with reply/star/ sync-destination actions gracefully disabled for free, no special-casing needed there. Scope, deliberately: only shown on the plain, unscoped "Posts" feed (same condition recentPostsTabsView uses to show its own tabs), since a federated fetch supports none of the author-scoping, text search, or PostsBeforeDate cutoff filtering a real GetPosts request does -- showing it anywhere those apply would be misleading. Fetched once at page load, not re-fetched on tab/search/cutoff changes (none of which would change the result), and only re-filtered by model.context at merge time in syncAnimations, since that's the one dimension that can still apply to an already-fetched federated feed. Verified with `make test` (all green) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Let Mastodon instances be browsed anonymously, no account needed Adds a "just browse this instance's public timeline" affordance (UI.mastodonBrowseSection) alongside the existing OAuth "Connect" flow -- type a host, add it, no popup, no admin-registered app, no account at all, since Mastodon.fetchPosts already hits a plain unauthenticated GET (it never used the connected account's accessToken in the first place). Mirrors the servers strip's own "type a host, add it" shape for real Jonline servers, minus the connectivity validation a real server add does -- there's nothing to check ahead of time, a bad host just silently fails to load posts. AccountsPanel.browsedMastodonInstances is a bare List String (no richer record needed -- there's no connection state or credential to track). PostsPage.fetchFederatedPosts now fetches the deduplicated union of connected accounts' instanceHosts and browsed instances, since both hit the exact same endpoint -- no reason to fetch a host twice just because it's both connected and browsed. Verified with `make test` (all green) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix startup panic on legacy federation_info + wire Mastodon defaults Bug: any existing server_configurations row persisted before mastodon_servers existed panicked the whole server on startup -- ToProtoServerConfiguration::to_proto's serde_json::from_value(...) .unwrap() hit "missing field mastodon_servers". Unlike facebook_auth_config/x_twitter_auth_config (both optional, so serde already treats a missing key as None for free), mastodon_servers is a repeated field (Vec<MastodonServer>), which serde treats as a hard error when absent. Fixed with the same #[serde(default)] field_attribute this codebase already used for exactly this problem once before (EventSettings' two newest fields, see build.rs) -- added a regression test alongside the existing custom_tabs migration tests proving legacy federation_info still deserializes. Also wires up MastodonServer.configured_by_default/pinned_by_default, which existed in the proto but had no client behavior yet: the first time a browsing host is ever seen (same moment FederatedServer's own configured_by_default/pinned_by_default already auto-connects real federated servers), any MastodonServer marked either flag now gets added to browsedMastodonInstances automatically -- no negotiation needed, unlike a real server, since browsing an instance is just an unauthenticated GET. Documented (in both the proto and the Elm code) that the two flags aren't currently distinguishable for Mastodon, since browsedMastodonInstances has no disabled-but-present state the way Server.enabled gives pinned_by_default extra meaning over configured_by_default for real servers. Verified with `make test` (all green, including the new regression test) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Document Trans-Protocol Federation; fix federation accuracy in README Fills in the previously-empty "Trans-Protocol Federation" sections in both README.md and jonline.proto's doc comments (Mastodon/ActivityPub and BlueSky/AT Protocol), and corrects two related inaccuracies: - "Inter-Server Federation"/"Delightful Federation" described Federated Servers as a form of "server-to-server communication," then cited CORS as the safeguard against it -- CORS only matters for *client* cross-origin calls, confirming this is actually client-driven, not server-to-server. Reworded to say Jonline servers never talk to each other directly, and pointed to Sync Destinations as the one place the backend genuinely does initiate outbound calls to other platforms. - Added a "Jonline as a protocol vs. Bluesky/AT Protocol" section and a paragraph in the ActivityPub comparison addressing whether Jonline is "isomorphic to a superset of ActivityPub": true of the object model (Events/Groups/Media as first-class types), not true of the federation mechanism (no server-to-server delivery protocol at all). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Unify real-server and Mastodon/Bluesky post fetching under one FeedSource type PostsPage previously ran two entirely separate fetch-and-store pipelines: postsByServer/GotServerPosts/fetchNewServers/refetchServers for real Jonline servers, and federatedPosts/GotFederatedPosts/fetchFederatedPosts for Mastodon/Bluesky, merged only at the last step in syncAnimations. Introduces FeedSource (JonlineServer/MastodonInstance/BlueskyFeed) as the one type both paths now dispatch over, collapsing GotServerPosts/ GotFederatedPosts into a single GotFeedPosts Msg (normalized via a small FeedResult type, since Grpc.Error/GetPostsResponse and Http.Error/List Post have no content worth keeping once a fetch fails) and refetchServers/ fetchNewServers into refetchFeeds/fetchNewFeeds, generalized over FeedSource instead of just AccountsPanel.Server. federatedPosts as a separate Model field is gone -- Mastodon/Bluesky feeds now live in postsByServer under their existing synthetic host keys, using the same ServerFeed status tracking real servers get. Search text/date-cutoff refetches (applySearchChange, TabChanged, GotNow, PublishedBeforeDebounceElapsed) stay deliberately scoped to real servers only -- Mastodon/Bluesky support neither, so refetching them there would just be wasted, unfiltered-anyway network calls. One behavior change falls out of the unification for free: a newly-connected Mastodon instance/ Bluesky account now shows up live (via the same Poll/SharedMsg path real servers use) instead of only on this page's next (re)visit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Flesh out Mastodon/ActivityPub server config docs; rename to Cross-Protocol Federation Documents FederationInfo.mastodon_servers in jonline.proto's Server Configuration section: what it's for (registering a per-instance OAuth app so users can connect their own Mastodon account -- unlike anonymous browsing, which needs no config at all), why it's per-instance (Mastodon has no central OAuth authority the way Facebook/X do), and how configured_by_default/pinned_by_default mirror FederatedServer's own fields but govern anonymous browsing instead of account auto-connect. Also renames "Trans-Protocol Federation" to "Cross-Protocol Federation" throughout README.md/jonline.proto (and regenerated docs/bindings) -- "cross-protocol" is the more idiomatic term for the same feature. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add run_flutter, build_* targets, and a run_tmux dev launcher Fills out the run_backend/run_elm/run_tamagui set with run_flutter, and adds a parallel build_backend/build_elm/build_tamagui/build_flutter set -- tamagui/flutter had no plain `build` target yet, so those got thin aliases (tamagui's `build` -> existing `rebuild_fe`; flutter's `build` -> existing `build_web`) rather than duplicating logic. flutter also had no `run` target at all (added `fvm flutter run`, matching its own README's documented dev command). run_tmux launches backend (left) + Elm (right) in one tmux session with mouse mode on (click a pane to focus/resize instead of needing tmux's own keybindings) and pane-border-status titles labeling which is which. automatic-rename is turned off and the window/terminal title pinned to "jonline: run_tmux", since tmux otherwise relabels the window after whatever's currently in the foreground (make, then cargo/npx, etc.). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Catch a missed case variant in the Trans-Protocol -> Cross-Protocol rename The earlier rename's sed patterns matched "Trans-Protocol Federation" and "trans-protocol federation" but missed the mid-sentence, sentence-cased "Trans-protocol federation" in the BlueSky/AT Protocol doc paragraph. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * update README * docs and stuff --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Commit:62dab1c
Author:Jon Latane

docs and stuff

Commit:645313f
Author:Jon Latane

Catch a missed case variant in the Trans-Protocol -> Cross-Protocol rename The earlier rename's sed patterns matched "Trans-Protocol Federation" and "trans-protocol federation" but missed the mid-sentence, sentence-cased "Trans-protocol federation" in the BlueSky/AT Protocol doc paragraph. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:640f5c6
Author:Jon Latane

Flesh out Mastodon/ActivityPub server config docs; rename to Cross-Protocol Federation Documents FederationInfo.mastodon_servers in jonline.proto's Server Configuration section: what it's for (registering a per-instance OAuth app so users can connect their own Mastodon account -- unlike anonymous browsing, which needs no config at all), why it's per-instance (Mastodon has no central OAuth authority the way Facebook/X do), and how configured_by_default/pinned_by_default mirror FederatedServer's own fields but govern anonymous browsing instead of account auto-connect. Also renames "Trans-Protocol Federation" to "Cross-Protocol Federation" throughout README.md/jonline.proto (and regenerated docs/bindings) -- "cross-protocol" is the more idiomatic term for the same feature. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:3c8cab6
Author:Jon Latane

Document Trans-Protocol Federation; fix federation accuracy in README Fills in the previously-empty "Trans-Protocol Federation" sections in both README.md and jonline.proto's doc comments (Mastodon/ActivityPub and BlueSky/AT Protocol), and corrects two related inaccuracies: - "Inter-Server Federation"/"Delightful Federation" described Federated Servers as a form of "server-to-server communication," then cited CORS as the safeguard against it -- CORS only matters for *client* cross-origin calls, confirming this is actually client-driven, not server-to-server. Reworded to say Jonline servers never talk to each other directly, and pointed to Sync Destinations as the one place the backend genuinely does initiate outbound calls to other platforms. - Added a "Jonline as a protocol vs. Bluesky/AT Protocol" section and a paragraph in the ActivityPub comparison addressing whether Jonline is "isomorphic to a superset of ActivityPub": true of the object model (Events/Groups/Media as first-class types), not true of the federation mechanism (no server-to-server delivery protocol at all). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:589fc7b
Author:Jon Latane

Fix startup panic on legacy federation_info + wire Mastodon defaults Bug: any existing server_configurations row persisted before mastodon_servers existed panicked the whole server on startup -- ToProtoServerConfiguration::to_proto's serde_json::from_value(...) .unwrap() hit "missing field mastodon_servers". Unlike facebook_auth_config/x_twitter_auth_config (both optional, so serde already treats a missing key as None for free), mastodon_servers is a repeated field (Vec<MastodonServer>), which serde treats as a hard error when absent. Fixed with the same #[serde(default)] field_attribute this codebase already used for exactly this problem once before (EventSettings' two newest fields, see build.rs) -- added a regression test alongside the existing custom_tabs migration tests proving legacy federation_info still deserializes. Also wires up MastodonServer.configured_by_default/pinned_by_default, which existed in the proto but had no client behavior yet: the first time a browsing host is ever seen (same moment FederatedServer's own configured_by_default/pinned_by_default already auto-connects real federated servers), any MastodonServer marked either flag now gets added to browsedMastodonInstances automatically -- no negotiation needed, unlike a real server, since browsing an instance is just an unauthenticated GET. Documented (in both the proto and the Elm code) that the two flags aren't currently distinguishable for Mastodon, since browsedMastodonInstances has no disabled-but-present state the way Server.enabled gives pinned_by_default extra meaning over configured_by_default for real servers. Verified with `make test` (all green, including the new regression test) and `yarn tsc` before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:ed2ca3b
Author:Jon Latane

Prep work for trans-protocol support

Commit:b854e6c
Author:Jon Latané
Committer:GitHub

Simplify kubernetes background jobs, event filter admin setting, simplify federated auth (#101) * K8s fixes * show_started_or_long_events_by_default admin flag * docs * simplify auth tokens * more stuff * calendar rendering and docs

Commit:1d539bd
Author:Jon Latane

calendar rendering and docs

Commit:599327d
Author:Jon Latane

more stuff

Commit:6f1b20e
Author:Jon Latane

simplify auth tokens

Commit:c356da6
Author:Jon Latane

docs

Commit:5b5a7fe
Author:Jon Latane

show_started_or_long_events_by_default admin flag

Commit:e0788bf
Author:Jon Latane

Merge branch 'main' into simplify-kubernetes-background-jobs

Commit:26d7013
Author:Jon Latane

K8s fixes

Commit:e0fba7f
Author:Jon Latané
Committer:GitHub

AI Image Generator, eliminate separate event_ids and event_instance_ids (all are just post_ids) (#100) * revamp * docs * Gemini Nano Banana/OpenAI Image image generation * capability awareness, overage and other refinements * UserPreferences postsBefore and eventsAfter defaults * more rigorous custom tab stuff * Collapse events.id/event_instances.id into their post_id Events and EventInstances each carried a redundant surrogate id alongside a post_id that was already a required, unique 1:1 FK to posts. Drops the surrogates and makes post_id the primary key for both tables, removes the now-redundant Event.id/EventInstance.id/GetEventsRequest.event_id/ event_instance_id proto fields (post_id is a strict superset of the last two), and propagates the identity change through the Rust backend, Elm SPA (Pages/Event/EventId_.elm -> PostId_.elm), and Tamagui frontend. Flutter needed no changes -- its event screens are unwired scaffolding. Also fixes two search_text propagation triggers that independently joined events by its old surrogate id. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add short Post/Event URLs (/{postId}) Any path segment starting with a character no username or custom tab path could legally start with (-._~:/?[]@!$&'()*+,;%=, deliberately excluding # since fragments never reach the server) now resolves as a short alias for /post/:id or /event/:id -- rendering the same content in place without redirecting the address bar. Enforced server-side in validate_username/validate_custom_tab_path so new usernames/custom tabs can never collide with the reserved set. Elm: extracts Components/Pages/EventPage.elm out of Pages/Event/PostId_.elm (mirroring the existing PostPage split), adds Components/Pages/PostOrEventPage.elm to disambiguate and embed the right one, and wires the dispatch into UsernameOrCustomTab_.elm. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * run make * Remove dead UI.CustomNav.homeTarget and unused CalendarDisplayMode alias Pre-existing elm-review failures (make test_elm), unrelated to recent work: homeTarget was fully superseded by homeConfig with no remaining callers, and the CalendarDisplayMode module alias had no qualified uses left. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Commit:5ee950b
Author:Jon Latane

Add short Post/Event URLs (/{postId}) Any path segment starting with a character no username or custom tab path could legally start with (-._~:/?[]@!$&'()*+,;%=, deliberately excluding # since fragments never reach the server) now resolves as a short alias for /post/:id or /event/:id -- rendering the same content in place without redirecting the address bar. Enforced server-side in validate_username/validate_custom_tab_path so new usernames/custom tabs can never collide with the reserved set. Elm: extracts Components/Pages/EventPage.elm out of Pages/Event/PostId_.elm (mirroring the existing PostPage split), adds Components/Pages/PostOrEventPage.elm to disambiguate and embed the right one, and wires the dispatch into UsernameOrCustomTab_.elm. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:53a1223
Author:Jon Latane

Collapse events.id/event_instances.id into their post_id Events and EventInstances each carried a redundant surrogate id alongside a post_id that was already a required, unique 1:1 FK to posts. Drops the surrogates and makes post_id the primary key for both tables, removes the now-redundant Event.id/EventInstance.id/GetEventsRequest.event_id/ event_instance_id proto fields (post_id is a strict superset of the last two), and propagates the identity change through the Rust backend, Elm SPA (Pages/Event/EventId_.elm -> PostId_.elm), and Tamagui frontend. Flutter needed no changes -- its event screens are unwired scaffolding. Also fixes two search_text propagation triggers that independently joined events by its old surrogate id. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:27ab847
Author:Jon Latane

more rigorous custom tab stuff

Commit:7975115
Author:Jon Latane

UserPreferences postsBefore and eventsAfter defaults

Commit:c0177cc
Author:Jon Latane

capability awareness, overage and other refinements

Commit:e12e3cd
Author:Jon Latane

Gemini Nano Banana/OpenAI Image image generation

Commit:781b4f0
Author:Jon Latane
Committer:Jon Latane

docs

Commit:5576477
Author:Jon Latane

revamp

Commit:0f5a9f9
Author:Jon Latane

sync improvements, macOS+Linux package deploys, jonline 0.5.553

Commit:8d813a3
Author:Jon Latane

small updates

Commit:ae4e90a
Author:Jon Latané
Committer:GitHub

Add Instagram, Threads, X/Twitter, Mastodon and Bluesky SyncDestinations (#99) * Add vibecoded Instagram, X/Twitter, Mastodon and Bluesky SyncDestination support * add threads support * updates * another sweet of cleanup * update docs * more docs updates

Commit:00c1e53
Author:Jon Latane

update docs

Commit:78d0b4c
Author:Jon Latane

another sweet of cleanup

Commit:6b00045
Author:Jon Latane

updates

Commit:1e84e0c
Author:Jon Latane

add threads support

Commit:dedcbe1
Author:Jon Latane
Committer:Jon Latane

Add vibecoded Instagram, X/Twitter, Mastodon and Bluesky SyncDestination support

Commit:f88f6fe
Author:Jon Latané
Committer:GitHub

EventSyncDestinations -> SyncDestinations, sync Posts to SyncDestinations, plus some Settings cleanup (#98) * clean up settings * convert EventSyncDestination to SyncDestination and add support for Posts * fix Flutter/React

Commit:9912063
Author:Jon Latane

convert EventSyncDestination to SyncDestination and add support for Posts

Commit:c89d8c1
Author:Jon Latane

Custom Home pages, fancier post layouts

Commit:ec49886
Author:Jon Latane

Better calendar preview modal UX, hide visibility and server on posts/events when unneeded

Commit:839fced
Author:Jon Latané
Committer:GitHub

Elm: Advanced Event Editing (#97) * decompose UpdateEvent RPC * run make * Elm instance create/update * crud for event instances * disable editing synced event instance stuff

Commit:d85b044
Author:Jon Latane

decompose UpdateEvent RPC

Commit:d009c86
Author:Jon Latane

docs and ux tweaks

Commit:9a93b5c
Author:Jon Latane

single-server, multi-account push

Commit:6c194c2
Author:Jon Latané
Committer:GitHub

Message Push Notifications (#95) * web push initial pass * cleanup old BE stuff, add FE stuff

Commit:397bceb
Author:Jon Latane

web push initial pass

Commit:cf1d6a4
Author:Jon Latane

messaging tweaks

Commit:ebd5e94
Author:Jon Latané
Committer:GitHub

add aspect ratios to all media (#93)

Commit:f7df22f
Author:Jon Latane

add aspect ratios to all media

Commit:3d8ada2
Author:Jon Latane

more Stalwart fixes

Commit:0e6dab0
Author:Jon Latane

fix stalwart integration

Commit:0ad3a40
Author:Jon Latané
Committer:GitHub

Elm: Messaging (Emails) UI (#89) * most of messaging * unread/read marking for messaging * elm-review fixes * make * readme updates

Commit:7db03cd
Author:Jon Latane

unread/read marking for messaging

Commit:4795d92
Author:Jon Latane

docs and Tamagui tests

Commit:bc6f0d8
Author:Jon Latané
Committer:GitHub

Elm: Custom navigation tabs (#88) * improve some CSS, disable editing synced event fields * CSS tweaks * add rust tdd stuff * custom nav 0.5 * full custom tabs * Final bits * review fix * docs * docs

Commit:296514c
Author:Jon Latane

docs

Commit:3de24e8
Author:Jon Latane

docs

Commit:b34268d
Author:Jon Latane

Final bits

Commit:0b721f4
Author:Jon Latane

custom nav 0.5

Commit:3057a5b
Author:Jon Latané
Committer:GitHub

Elm review and flutter fixes (#86) * please elm-review gods * bump ci elm version * flutter building again * more stuff * fix elm test in CI * more CI fixesg * fixes

Commit:6113e48
Author:Jon Latane

more stuff

Commit:ae80544
Author:Jon Latané
Committer:GitHub

v0.5.552: Updated protocol/ServerConfiguration for calendar display/lookback, latest Rust, elm-review fixes (#85) * some proto updates * protocol updates for ServerConfiguration feature settings * more stuff * Shape AccountsPanel better * Rust 2024, dependency updates * Flutter version pinning, elm-review fixes, some unification of Make targets - claude limit lol * CI updates * stuff

Commit:7ffa4ab
Author:Jon Latane

protocol updates for ServerConfiguration feature settings

Commit:399ef89
Author:Jon Latane

some proto updates

Commit:10077b7
Author:Jon Latane

css tweaks

Commit:464965f
Author:Jon Latané
Committer:GitHub

FB event is post fix (#84)

Commit:e42cf3d
Author:Jon Latane

prepare to get weird

Commit:7bff9b1
Author:Jon Latané
Committer:GitHub

Event Sync Destinations: Facebook (#83) * user visibility * more server config * event push * tweaks * fix elm

Commit:ec5f14b
Author:Jon Latane

tweaks

Commit:35a2810
Author:Jon Latane

event push

Commit:1be9c98
Author:Jon Latane

more server config

Commit:58216b9
Author:Jon Latané
Committer:GitHub

In-app Messaging, Event Sync Destinations (to Facebook Pages), robust user delete, better favicons (#82) * some stuff * Claude's first pass on messaging * FB event destination foundations, user delete robustness * a few small tweaks * simplify things * configurable on-server * the rest of the stuff * docs tweaks * add minio to rust test env in CI * autocreate MinIO buckets * attempt to resolve some warnings * more CI improvements

Commit:f6bec18
Author:Jon Latane

the rest of the stuff

Commit:af40fe7
Author:Jon Latane

configurable on-server

Commit:068c0d8
Author:Jon Latane

FB event destination foundations, user delete robustness

Commit:df641ca
Author:Jon Latane

Claude's first pass on messaging