Files
LegacyHUB/docs/dispatch-contract.md
Vadim Malanov d27dd0ffbb feat: align LegacyHUB with TeamHUB platform contract (D2/D4, assets, security)
Close 12 audit-driven platform-compliance gaps on a single branch.

- D4 dispatch: app/integrations/dispatch_client.py participant `legacyhub`,
  emits LegacyhubDocumentIndexed + AssetDerivativeReady after the indexing
  commit (idempotent uuid5), http_inbox route (reindex/tombstone) with
  audit-based dedupe; docs/dispatch-contract.md. Celery+Redis stays intra-module.
- D2 SSO: app/integrations/identity.py validates X-TeamHub-* + role/scope
  mapper; security.py adds trusted-header enforcement (AUTH_REQUIRE_IDENTITY)
  and a scope check on /search; docker-compose.teamhub.yml (external teamhub_net
  + internal db net, api not host-published); RUNBOOK network/firewall section.
- Asset standard: SearchHit/Citation carry asset_id/owner_module; buckets
  renamed teamhub-legacyhub-* (+quarantine/tmp/exports); purge-by-asset_id with
  legal-hold guard (app/indexing/projection.py); OCR-markdown derivative event.
- audit_log model + Alembic 0003 + record_audit on writes (same transaction).
- Secret masking: app/common/json_logger.py recursive mask wired into structlog
  (+ensure_ascii=False); event payloads redacted before persistence.
- Service X-API-Key mandatory on ingest endpoints (defence-in-depth).
- Port: host API 8000->8050 (collision with SalesHUB/MailHUB resolved),
  container still listens on 8000.
- Config: no plaintext secret defaults; fail-loud in non-dev (no value leak).
- Docs drift: README PG 5440, layered-auth note, 5173 removed from CORS;
  ingest/folder gated by ENABLE_FOLDER_INGEST (410 by default).
- ADRs: layers mapping, shared-core extraction, UI locale (RU-first).

Tests: 78 passing (ruff, compileall, pytest, tsc, vite build, compose config).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:44:15 +03:00

2.4 KiB

LegacyHUB — dispatch participant contract

Status: draft · 2026-06-15 · D4 / 08_DECISIONS, INTER_MODULE_CONTRACT §2

LegacyHUB joins the shared platform RabbitMQ via dispatch-api (not directly). Internal background work stays on Celery+Redis; this contract covers only the inter-module bus.

Participant

Field Value
participant_code legacyhub
participant_type service
Auth to dispatch-api X-API-Key (env DISPATCH_API_KEY)
Delivery mode (inbound) http_inboxPOST /api/v1/dispatch/inbox + business-confirm
Inbox auth service X-API-Key (require_service_api_key)

Configuration: DISPATCH_ENABLED, DISPATCH_API_URL, DISPATCH_API_KEY, DISPATCH_PARTICIPANT_CODE, DISPATCH_TIMEOUT_SECONDS.

Envelope (INTER_MODULE_CONTRACT §2.4)

{
  "message_id": "<uuid4, unique per send>",
  "event_id": "<uuid5 over the business key, idempotent>",
  "card_uid": "<asset_id or document_id>",
  "message_type": "<Module><Event>",
  "participant_code": "legacyhub",
  "created_at": "<ISO-8601 UTC>",
  "body": { "...": "JSON only — no binary, no presigned URLs, no local paths" }
}

Published events

message_type When Body (JSON)
LegacyhubDocumentIndexed After a document reaches INDEXING_COMPLETED (post-commit) document_id, and for asset ingests asset_id, owner_module, owner_record_type, owner_record_id, manifest_version
AssetDerivativeReady After OCR/markdown derivative is produced for an asset ingest (respects ingest_options.emit_events) event_type=AssetDerivativeReady, asset_id, owner_*, derivative_type=ocr_markdown, content_type=text/markdown, source_asset_sha256, generator{pipeline,version}

Idempotency: event_id = uuid5(namespace, business-key), so re-emits of the same indexing/derivative are de-dupable by consumers.

Subscriptions (inbound)

message_type Action
ReindexRequested / LegacyhubReindexRequested Resolve document_id (directly or via asset_id) and enqueue the Celery reindex task

Inbound delivery is at-least-once; the inbox is idempotent by event_id (recorded in audit_log as dispatch.inbox.processed).

Boundaries

  • No direct RabbitMQ from this module — only dispatch-api.
  • No binary payloads / presigned URLs / local filesystem paths in any body (10 §7). Originals stay owner-module-owned; LegacyHUB references object storage
    • asset_id only.