# Shared Shared infrastructure, models, contracts, services, and templates used by all five Rose Ash microservices (blog, market, cart, events, federation). Included as a git submodule in each app. ## Structure ``` shared/ db/ base.py # SQLAlchemy declarative Base session.py # Async session factory (get_session, register_db) models/ # Canonical domain models user.py # User magic_link.py # MagicLink (auth tokens) (domain_event.py removed — table dropped, see migration n4l2i8j0k1) kv.py # KeyValue (key-value store) menu_item.py # MenuItem (deprecated — use MenuNode) menu_node.py # MenuNode (navigation tree) container_relation.py # ContainerRelation (parent-child content) ghost_membership_entities.py # GhostNewsletter, UserNewsletter federation.py # ActorProfile, APActivity, APFollower, APFollowing, # RemoteActor, APRemotePost, APLocalPost, # APInteraction, APNotification, APAnchor, IPFSPin contracts/ dtos.py # Frozen dataclasses for cross-domain data transfer protocols.py # Service protocols (Blog, Calendar, Market, Cart, Federation) widgets.py # Widget types (NavWidget, CardWidget, AccountPageWidget) services/ registry.py # Typed singleton: services.blog, .calendar, .market, .cart, .federation blog_impl.py # SqlBlogService calendar_impl.py # SqlCalendarService market_impl.py # SqlMarketService cart_impl.py # SqlCartService federation_impl.py # SqlFederationService federation_publish.py # try_publish() — inline AP publication helper stubs.py # No-op stubs for absent domains navigation.py # get_navigation_tree() relationships.py # attach_child, get_children, detach_child widget_registry.py # Widget registry singleton widgets/ # Per-domain widget registration infrastructure/ factory.py # create_base_app() — Quart app factory cart_identity.py # current_cart_identity() (user_id or session_id) cart_loader.py # Cart data loader for context processors context.py # Jinja2 context processors jinja_setup.py # Jinja2 template environment setup urls.py # URL helpers (blog_url, market_url, etc.) user_loader.py # Load current user from session http_utils.py # HTTP utility functions events/ bus.py # emit_activity(), register_activity_handler() processor.py # EventProcessor (polls ap_activities, runs handlers) handlers/ # Shared activity handlers container_handlers.py # Navigation rebuild on attach/detach login_handlers.py # Cart/entry adoption on login order_handlers.py # Order lifecycle events ap_delivery_handler.py # AP activity delivery to follower inboxes (wildcard) utils/ __init__.py calendar_helpers.py # Calendar period/entry utilities http_signatures.py # RSA keypair generation, HTTP signature signing/verification ipfs_client.py # Async IPFS client (add_bytes, add_json, pin_cid) anchoring.py # Merkle trees + OpenTimestamps Bitcoin anchoring webfinger.py # WebFinger actor resolution browser/ app/ # Middleware, CSRF, errors, Redis caching, authz, filters templates/ # ~300 Jinja2 templates shared across all apps containers.py # ContainerType, container_filter, content_filter helpers config.py # YAML config loader log_config/setup.py # Logging configuration (JSON formatter) static/ # Shared static assets (CSS, JS, images, FontAwesome) editor/ # Koenig (Ghost) rich text editor build alembic/ # Database migrations ``` ## Key Patterns - **App factory:** All apps call `create_base_app()` which sets up DB sessions, CSRF, error handling, event processing, logging, widget registration, and domain service wiring. - **Service contracts:** Cross-domain communication via typed Protocols + frozen DTO dataclasses. Apps call `services.calendar.method()`, never import models from other domains. - **Service registry:** Typed singleton (`services.blog`, `.calendar`, `.market`, `.cart`, `.federation`). Apps wire their own domain + stubs for others via `register_domain_services()`. - **Activity bus:** `emit_activity()` writes to `ap_activities` table in the caller's transaction. `EventProcessor` polls pending activities and dispatches to registered handlers. Internal events use `visibility="internal"`; federation activities use `visibility="public"` and are delivered to follower inboxes by the wildcard delivery handler. - **Widget registry:** Domain services register widgets (nav, card, account); templates consume via `widgets.container_nav`, `widgets.container_cards`. - **Cart identity:** `current_cart_identity()` returns `{"user_id": int|None, "session_id": str|None}` from the request session. ## Alembic Migrations All apps share one PostgreSQL database. Migrations are managed here and run from the blog app's entrypoint (other apps skip migrations on startup). ```bash alembic -c shared/alembic.ini upgrade head ```