Services
10 microservices compose the Karmyq platform.
Auth Service
:3001Foundation service - no dependencies on other services. Sprint 132 PR A (ADR-099): auth.user_tags is the matching skill source, with nullable skill_slug referencing auth.skill_vocabulary. Legacy /users/:userId/skills routes removed; auth.user_skills retained only for rollback.
Community Service
:3002Sprint 47: Group Communities — community_type field, activities + activity_participants tables, activities API endpoints, activity_joined event
Request Service
:3003Server-driven UI: Schema API provides dynamic form definitions. Admin API enables zero-code request type creation. Sprint 43: feed_events table logs impressions/outcomes for weight tuning (ADR-048). match_completed uses karmyq-completion-dispatch with stable jobs fanned out to three service-specific completion queues; legacy handlers remain.
Reputation Service
:3004Sprint 126 (ADR-096): the match_completed payload is UNCHANGED, but reputation's projection of it is now atomic, idempotent, and timestamp-preserving. One transaction per match under pg_advisory_xact_lock; all writes ON CONFLICT DO NOTHING against uq_karma_match_projection / uq_activity_match_projection, so redelivering a job re-writes nothing. Karma is awarded in at most 3 shared request communities, selected from history STRICTLY BEFORE (completed_at, match_id). Rows carry the stored matches.completed_at, never NOW(). The payload is treated as a message, not a record: participants and status are re-read from the database under the lock and a disagreeing payload is rejected. Badges, provider metrics and trust evolution remain subscriber-owned and live-only — the historical backfill (npm run backfill:standing, dry-run by default) never replays them. Sprint 128 (PR C): the backfill PREVIEW derives its trust inputs from the projected post-apply karma rows rather than the replayed match list, so analyzeStandingBackfill scoreBuckets and providerEligibility match what updateTrustScore then stores. Breadth is global, so a membership with no local history but activity elsewhere scores 1, not 0; a member with no history anywhere still scores 0, and 1 is not a floor. providerEligibility counts provider-profile/community pairs keyed provider_id|community_id, and its 1/20/40/60 floors remain fixed what-if scenarios rather than the community configured provider_min_personal_trust_score. No formula, floor, filter, endpoint, schema or event change. Sprint 131 D9: the effective-params cache client is ioredis 6 (nested in this workspace; bull stays on ioredis 5), RESP3 with RESP2 fallback, v5 retry backoff pinned so Redis-outage fallback timing is unchanged. match_completed uses karmyq-completion-reputation alongside the retained legacy handler; standing policy is unchanged.
Notification Service
:3005Subscribes to platform events. directed_request_created uses only karmyq-directed-notifications; live lookup requires open/unexpired asks; terminal jobs are acknowledged without a new invitation. Idempotent per user/request inserts and outbox delivery acknowledgement prevent duplicates/lost intent. ADR-099 accepts notification-service updating only delivered_at in request-service-owned inventory.borrow_notification_outbox. match_completed uses karmyq-completion-notification; per-user/match transaction locks prevent duplicate completion notifications on partial recipient retries.
Messaging Service
:3006Cleanup Service
:3008CANDIDATE FOR REPLACEMENT: Could be replaced with pg_cron scheduled jobs. Scheduled jobs: dibs expiry (5m), match reminders (15m), mark-expired (hourly), hard-delete (2am), reputation decay (3am — NO-OP since Sprint 126/ADR-096: it overwrote reputation.trust_scores.score with a pre-ADR-037 karma/10 formula and would have wiped the standing backfill nightly; trust scores are owned by reputation-service), memoryRetentionJob (3:30am, Sprint 90/ADR-069 — anonymizes completed-exchange free-text to '[forgotten]' sentinels + cascade-forgets messages, hard-deletes expired/unmatched requests; karma_records left untouched; per-community retention windows via request_communities → retention_config), trust edge sweep (4:30am), activity log cleanup (weekly), decay report (weekly). Sprint 90 RETIRED requestTtlSweepJob (it hard-deleted completed+rated exchanges at 30d, destroying aggregates before the anonymize window — superseded by memoryRetentionJob). Governed by requests.retention_config.
Geocoding Service
:3009Sprint 109 / ADR-080: retained as shared PostgreSQL geocoding cache and external Nominatim policy boundary. Frontend consumes via apps/frontend/src/lib/geocoding.ts.
Social Graph Service
:3010Trust-first design foundation. Sprint 27: match_completed → social_graph.connections. Sprint 65: match_completed → social_graph.trust_edges. Sprint 67: ego-network rewrite — GET /trust/graph (aggregate) + GET /trust/graph/:communityId (community ego-network). Sprint 68: intrinsic Ebbinghaus decay via trust_edges_live view + decay config endpoints. Sprint 90: visible decay (ADR-070) — graph edges carry decayTier; new /trust/me/memory + /trust/relationships/fading endpoints; classifyDecayTier lives in @karmyq/shared. Governance endpoints are in community-service. match_completed uses karmyq-completion-social-graph alongside the retained legacy handler; graph counters remain at-least-once effects.
Simulation Service
:Development/demo tool only - not deployed to production. Sprint 117: owns the curated demo reset (deterministic protected-core baseline via a guarded transactional reset) plus the ambient synthetic population. Trust/karma history is fixture-only replay locked to production math; live match_completed behaviour is unchanged. Operator CLIs reset:demo/verify:demo/rotate:demo-stories are dry-run/read-only by default.