Commit Graph

90 Commits

Author SHA1 Message Date
giles
a93a456ac5 Remove sso_hint cookie, add sso-clear logout chain
sso_hint on .rose-ash.com was blocked by Safari ITP — the exact
problem we're solving. Replaced with redirect chain: account logout
chains through each client app's /auth/sso-clear to clear all
first-party sessions without any cross-domain cookies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:17:22 +00:00
giles
223491fad5 SSO revocation: clear local session when sso_hint cookie is gone
When account logs out and deletes sso_hint, client apps now detect
the missing cookie and clear their local session on next request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:15:35 +00:00
giles
dfc41ada7d Make account the OAuth authorization server instead of federation
All client apps (including federation) now redirect to account for OAuth.
Factory excludes account from OAuth client blueprint registration.
SSO logout chains through account instead of federation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:53:34 +00:00
giles
60cd08adc9 Add /auth/clear endpoint to reset session cookies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:44:47 +00:00
giles
d50f01d41f Logout: redirect through federation sso-logout to clear all sessions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:31:44 +00:00
giles
bfd8d55f27 Silent SSO via sso_hint cookie
- Federation sets sso_hint=1 on .rose-ash.com after magic link login
- Client apps: before_request checks sso_hint, triggers silent OAuth
  once per session (sso_checked flag prevents loops)
- Logout clears sso_hint cookie on all apps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:23:26 +00:00
giles
d0a5170cd9 Fix logout: redirect to blog home to avoid re-auth loop
Account's / requires login, so redirecting there after logout
triggers silent OAuth re-authentication. Blog home is safe.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:15:14 +00:00
giles
8323c45711 Fix logout: post to local /auth/logout/ not federation
Each app has its own session and OAuth logout endpoint now.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:07:03 +00:00
giles
16df62e2c4 Sign-in button → account app, clear old shared cookie
- Nav sign-in links point to account_url('/') instead of login_url()
- After-request hook clears old blog_session cookie on .rose-ash.com
  (prevents collision with new per-app first-party cookies)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:56:56 +00:00
giles
ea35e040e7 Fix OAuth authorize URL: include /auth prefix
The federation auth blueprint is mounted at /auth, so the authorize
endpoint is /auth/oauth/authorize, not /oauth/authorize.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:25:46 +00:00
giles
46f44f6171 OAuth SSO infrastructure + account app support
- OAuthCode model + migration for authorization code flow
- OAuth client blueprint (auto-registered for non-federation apps)
- Per-app first-party session cookies (fixes Safari ITP)
- /oauth/authorize endpoint support in URL helpers
- account_url() helper + Jinja global
- Templates: federation_url('/auth/...') → account_url('/...')
- Widget registry: account page links use account_url

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:55:27 +00:00
giles
326b380135 Fix root top-bar account link to point to federation
The logged-in user links in _full_user.html (desktop + mobile)
still used blog_url — now they use federation_url to match
the account page migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:07:38 +00:00
giles
ea8e7da9d4 Move account page URLs from blog to federation
Auth templates and widget nav links now point to
federation_url instead of blog_url, co-locating the
account UI with the auth system in the federation app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:00:44 +00:00
giles
bd18d0befc Route auth to federation: login_url default, logout URL, federation_url global
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:41:18 +00:00
giles
5bed4a6c78 Update README: coop_url → blog_url
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:35:16 +00:00
giles
798087de9a Rename coop to blog throughout codebase
- coop_url() → blog_url(), AUTH_APP default → "blog"
- Session cookie: coop_session → blog_session
- Config keys: coop_root/coop_title → market_root/market_title
- All Jinja templates: coop_url → blog_url, coop_title → market_title
- Template blocks: coop-child-header → blog-child-header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:32:46 +00:00
giles
cc22b21b18 Rename coop.rose-ash.com to blog.rose-ash.com in comment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:27:54 +00:00
giles
f085d4a8d0 Add search_actors to FederationService for paginated actor search
Fuzzy ILIKE search across remote actors and local profiles, with
WebFinger resolution for @user@domain queries. Supports page-based
pagination for infinite scroll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:18:43 +00:00
giles
b16ba34b40 Add list_marketplaces to MarketService protocol, impl, and stub
Paginated query for market listings — supports optional container filtering
and returns (dtos, has_more) for infinite scroll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:29:14 +00:00
giles
16e4d3aa57 Make upcoming_entries_for_container work without container filter
When container_type/container_id are None, returns all upcoming
confirmed entries across all calendars (for global event listings).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:04:55 +00:00
giles
6e438dbfdc Add upcoming_entries_for_container to CalendarService
New paginated query for upcoming confirmed entries across all calendars
belonging to a container (page). Used by the events page summary view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 22:28:18 +00:00
giles
7316dc6eac Add 'updated' timestamp to Update activity objects for Mastodon
Mastodon requires an updated field to process post edits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:41:18 +00:00
giles
a3a41dbefd Allow repeated Update activities for post edits
The dedup guard was blocking legitimate edits after the first Update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:36:15 +00:00
giles
30b5a1438b Change AP_DOMAIN default to federation.rose-ash.com
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:12:28 +00:00
giles
0e89dbee55 Make origin_app migration idempotent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:59:19 +00:00
giles
86ccfd25c5 Add origin_app to APActivity — apps only process their own activities
Each app's EventProcessor now filters by origin_app so apps don't steal
each other's pending activities. emit_activity() and publish_activity()
auto-detect the app name from Quart's current_app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:57:46 +00:00
giles
b42f5d63db Add debug logging to EventProcessor and activity handler registry
Logs which handlers are registered at startup and which handlers are
found/called when processing each activity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:49:37 +00:00
giles
2e48760b38 Fix AP re-publish: use versioned object IDs after Delete
After Delete + re-Create, Mastodon tombstones the old object ID and
ignores new Creates with the same ID. Now appends /v2, /v3 etc. so
remote servers treat re-publishes as fresh posts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:59:48 +00:00
giles
9cdd2195df Restore all 33 deleted shared templates
Templates were incorrectly identified as dead code because individual
apps override them, but other apps still depend on the shared versions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:26:48 +00:00
giles
46f6ca4a0f Remove dead code: domain_event.py + 39 overridden templates
- Delete shared/models/domain_event.py (table dropped, model orphaned)
- Delete 39 shared templates that are overridden by app-local copies:
  - 8 blog overrides (blog/_action_buttons, post/_meta, etc.)
  - 27 events overrides (calendar/*, day/*, entry/*, post_entries/*)
  - 4 market overrides (market/index, browse/_oob_elements, etc.)

These shared copies were never served — Quart loads app-level
templates first, so the app-local versions always win.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:09:02 +00:00
giles
7de4a2e40e Remove dead shared _cart.html template
The cart app has its own override with ticket support. The shared
copy was never used and would only cause confusion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:05:17 +00:00
giles
1c1ab3576f Pass cart_sid through login URL for cross-app cart adoption
When login_url() is called from a different app (e.g. cart), the
anonymous cart_sid is in that app's session cookie. Pass it as a
query parameter so the auth app can store it and use it for adoption.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:46:08 +00:00
giles
07aa2e2be9 Fix cart sign-in button: use plain link instead of HTMX
The login URL is cross-origin (blog app), so hx-get can't load it
into the current page. Use a regular <a href> for cross-app navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:37:16 +00:00
giles
2e9db11925 Unify domain_events + ap_activities into AP-shaped event bus
All cross-service events now flow through ap_activities with a unified
EventProcessor. Internal events use visibility="internal"; federation
activities use visibility="public" and get delivered by a wildcard handler.

- Add processing columns to APActivity (process_state, actor_uri, etc.)
- New emit_activity() / register_activity_handler() API
- EventProcessor polls ap_activities instead of domain_events
- Rewrite all handlers to accept APActivity
- Migrate all 7 emit_event call sites to emit_activity
- publish_activity() sets process_state=pending directly (no emit_event bridge)
- Migration to drop domain_events table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:19:29 +00:00
giles
d697709f60 Tech debt cleanup: fix session.py, remove stale references, update docs
- db/session.py: fix indentation (2→4 space), pool_size=0 (unlimited),
  remove "ned to look at this" typo
- Remove glue.models from alembic env.py import list
- Update shared __init__.py, menu_item.py docstring, calendar_impl.py,
  handlers/__init__.py to remove glue terminology
- Remove federation_handlers.py tombstone file
- Remove TODO comments (replace with explanatory comments)
- Rewrite README.md to reflect current architecture
- Update anchoring.py TODO to plain comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:11:31 +00:00
giles
04f7c5e85c Add fediverse social features: followers/following lists, actor timelines
Adds get_followers_paginated and get_actor_timeline to FederationService
protocol + SQL implementation + stubs. Includes accumulated federation
changes: models, DTOs, delivery handler, webfinger, inline publishing,
widget nav templates, and migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:41:58 +00:00
giles
eec750a699 Fix AP object id: must be on actor's domain (Mastodon origin check)
Mastodon verifies the object id domain matches the actor domain.
Using the post URL (coop.rose-ash.com) as object id caused silent
rejection. Now always uses {activity_id}/object on federation domain.
Also adds to/cc on object for visibility determination.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:52:56 +00:00
giles
fd163b577f Inline federation publication + fix AP delivery
- Replace async federation_handlers with inline try_publish() at write sites
- Fix ap_delivery_handler: urlparse for signature path/host, @context array
  with security vocab, Delete/Tombstone object handling
- Fix federation_impl: @context array for IPFS, .limit(1) + upsert follower

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:24:29 +00:00
giles
3bde451ce9 Inline federation publication, remove async handlers
Federation activities are now created at write time via try_publish()
instead of relying on async event handlers. Fixes race condition where
multiple EventProcessors could consume post.published events in apps
that couldn't meaningfully process them.

AP delivery (federation.activity_created → inbox POST) stays async.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:54:14 +00:00
giles
798fe56165 Fix MultipleResultsFound crash in get_activity_for_source
- Use .scalars().first() + LIMIT 1 instead of scalar_one_or_none()
  which crashes when multiple activities exist for the same source
- Allow re-Create after Delete (re-publish after unpublish)
- Add missing on_post_unpublished handler to root shared copy
- Sync add_follower upsert fix to root shared

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:49:19 +00:00
giles
18410c4b16 Add unpublish (Delete) support + improve object IDs
- on_post_unpublished handler sends Delete/Tombstone activity
- Create/Update objects use post URL as id (for Delete reference)
- Delete objects use Tombstone type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:26:56 +00:00
giles
a28add8640 Add WARNING-level logging to federation publish handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:13:00 +00:00
giles
68941b97f6 Fix sign_request call in AP delivery handler
Parse inbox URL into path+host instead of passing url= which doesn't exist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:52:30 +00:00
giles
1d83a339b6 Upsert followers in add_follower to prevent IntegrityError
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:43:20 +00:00
giles
24432cd52a Page-aware labels in blog_new template
- "Post title"→"Page title", "Create Post"→"Create Page" when is_page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:31:58 +00:00
giles
9a1a4996bc Use "Page" labels instead of "Post" when editing pages
- Edit: placeholder "Page title..." vs "Post title..."
- Settings: slug placeholder, featured checkbox, custom template
  all say "page" when is_page is true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:13:50 +00:00
giles
1832c53980 Skip blog chrome (like, tags, authors, excerpt) for pages
Pages are container/landing pages, not blog posts. Hide the like
button, tags/authors bar, and excerpt when post.is_page is true.
Feature image and content still render for both.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:10:39 +00:00
giles
9db739e56d Fix adopt_entries_for_user deleting confirmed bookings on login
The adoption logic soft-deleted ALL user entries before adopting
anonymous session entries. This nuked confirmed/ordered bookings
every time the user logged in. Add state="pending" filter so only
stale pending entries are cleared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 21:19:42 +00:00
giles
dd7a99e8b7 Add federation event handlers, AP delivery, and anchoring
Phase 3-5 of ActivityPub integration:
- Federation handlers: post.published, calendar_entry.created, product.listed
  → publish_activity() for AP outbox
- AP delivery handler: federation.activity_created → sign + POST to follower
  inboxes with HTTP Signatures
- IPFS storage wired into publish_activity() (best-effort)
- Anchoring utility: merkle trees + OpenTimestamps Bitcoin timestamping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:57:31 +00:00
giles
8850a0106a Add federation/ActivityPub models, contracts, and services
Phase 0+1 of ActivityPub integration:
- 6 ORM models (ActorProfile, APActivity, APFollower, APInboxItem, APAnchor, IPFSPin)
- FederationService protocol + SqlFederationService implementation + stub
- 4 DTOs (ActorProfileDTO, APActivityDTO, APFollowerDTO, APAnchorDTO)
- Registry slot for federation service
- Alembic migration for federation tables
- IPFS async client (httpx-based)
- HTTP Signatures (RSA-2048 sign/verify)
- login_url() now uses AUTH_APP env var for flexible auth routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:10:08 +00:00