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>
2.4 KiB
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_inbox → POST /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_idonly.