MediaRouter / docs /unified-publishing.md
basyx's picture
Upload 794 files
1b2323a verified
|
Raw
History Blame Contribute Delete
7.78 kB

Unified Publishing Center

Architecture

The Publishing Center is a composition layer over app/social. It does not own provider connections, tokens, media files, publishing jobs, retries, or provider payload translation.

The authoritative flow is:

  1. A canonical SocialPost references a workspace project and Social media asset.
  2. Each exact connected account creates one SocialPostTarget.
  3. PublishingService validates account state, authorization scopes, provider capabilities, canonical asset ownership, project provenance, and adapter media constraints.
  4. Publish-now creates idempotent SocialJob records. Scheduling creates one durable SocialSchedule stored in UTC with its IANA display timezone.
  5. Existing Social workers claim jobs atomically and call the registered provider adapter.
  6. Provider results and reconciliation update each target independently. Post reconciliation reports published, failed, or partial_success.

Provider adapters remain the only components that translate canonical post data into provider API payloads. An adapter that is registered but does not advertise publishing remains unavailable and must raise SOCIAL_PROVIDER_NOT_IMPLEMENTED or SOCIAL_CAPABILITY_UNSUPPORTED; neither condition is converted to success.

Canonical post and targets

SocialPost now records optional immutable project provenance, a bounded canonical caption, and bounded canonical hashtags. Platform overrides remain on targets and never mutate the canonical copy.

Target input is strict. YouTube, TikTok, X, and LinkedIn use their existing typed provider metadata models. Providers without implemented publishing contracts are capability-gated instead of accepting arbitrary payloads.

No binary media or credentials are stored in Social records. A Social media asset binds an existing canonical MediaRouter asset to a validated immutable output locator.

Validation

POST /v1/social/posts/{post_id}/validate returns a structured result for every target. Validation reuses:

  • connected-account and OAuth scope checks;
  • provider capability metadata;
  • canonical Social media resolution;
  • existing FFprobe/media validation;
  • each provider adapter's validate_media implementation.

The publish and schedule paths revalidate server-side. Frontend validation is advisory and cannot authorize publishing.

Scheduling and jobs

Scheduling uses SocialSchedule; timestamps must be offset-aware and are stored as UTC. The supplied IANA timezone is retained for display. The existing scheduler claims due records using row locks and SKIP LOCKED. Publishing jobs use deterministic per-target idempotency keys and bounded worker concurrency.

Provider-native scheduling is used only by an adapter when explicitly supported. The Publishing Center otherwise relies on MediaRouter's durable scheduler. The separate Calendar interface is outside this phase.

Retries, reconciliation, and partial success

The existing worker retry classifier applies exponential backoff and jitter to transient errors. Invalid credentials, invalid media, permission denial, unsupported operations, cancellation, and non-retryable provider responses are not retried indefinitely. OAuth 401 handling refreshes once through OAuthService.

The Publishing Center also exposes a target-scoped retry endpoint. It only accepts a failed target with no external provider ID, rejects permanent configuration/auth/media failures, and derives a deterministic retry job key. It never requeues successful or uncertain provider operations.

Async provider acceptance is reconciled through the adapter's status or recovery contract. MediaRouter does not claim exactly-once external delivery where the provider cannot support lookup or idempotency; local idempotency prevents duplicate internal operations.

Targets reconcile independently. A mixture of published and failed targets produces partial_success, never published.

Cancellation

Draft, scheduled, queued, and retrying jobs can transition to cancelled. Preparing, processing, uploading, and publishing jobs instead record cancellation_requested_at. Their target remains in its actual state until the worker/provider result is reconciled. This prevents MediaRouter from claiming cancellation after a provider may have accepted a post.

Security, permissions, and RLS

The API uses existing Social scopes:

  • social:accounts:read / social:accounts:write
  • social:posts:read / social:posts:write
  • social:posts:publish
  • social:schedules:write

Project and asset ownership is checked server-side. The additive migration adds a project foreign key, a same-workspace integrity trigger, indexes, and retains forced RLS on posts, targets, and jobs. Social accounts, media references, targets, schedules, and jobs continue using the existing workspace policies. Runtime PostgreSQL RLS certification remains deferred.

Tokens are available only through TokenService. REST, SDK, MCP, n8n, logs, audits, and job payloads never expose access tokens, refresh tokens, provider secrets, authorization headers, or signed upload/session URLs.

Audit and retention

The Publishing Center uses existing bounded Social audit records, including post creation, validation, scheduling, publish start, completion/failure, and cancellation. Audit and application logs contain opaque resource identifiers, provider, status, duration, and error categories only.

Raw provider payloads are not retained by the composer. Provider recovery state that must survive restarts remains encrypted in the existing dedicated job field. Public result metadata is filtered before transport.

Frontend

Routes:

  • /publish
  • /projects/[projectId]/publish

TanStack Query owns providers, accounts, assets, posts, jobs, and validation responses. The composer keeps only draft selections in local UI state. It requires exact account selection, capability-gates unavailable adapters, registers an existing project output as a Social media reference when needed, and requires an accessible confirmation dialog before external publishing. Status uses the existing Social job polling rather than a second realtime system.

Project actions link to the project-aware route. Content Studio, AI Studio, and template-created projects use the same project/asset provenance and publishing APIs; there is no specialized publishing path.

Copilot

The shared Copilot action registry contains:

  • publishing.validate
  • publishing.create_post
  • publishing.schedule
  • publishing.publish
  • publishing.cancel

Publish, schedule, and cancellation are registered as external side effects and always require confirmation. Copilot uses exact post/account identifiers and the canonical Social services; it cannot invoke an adapter or arbitrary function directly.

SDK, MCP, and n8n

TypeScript and Python SDKs extend the existing Social resource with structured validation and the new canonical fields. Existing methods remain compatible.

MCP exposes typed validation and job listing alongside existing create, publish, schedule, cancel, and status tools. Authorization remains enforced by the MCP registry.

The existing n8n Social node adds List Posts, Validate Post, and List Publishing Jobs. Operations use the official SDK, preserve idempotency and Continue On Fail, and return real per-target backend state.

Provider certification boundary

Unit tests may mock provider boundaries. Live provider certification, live OAuth credentials, Docker, PostgreSQL runtime/RLS verification, FFmpeg runtime verification, and final production hardening remain deferred. An implementation_status of registered or a false publishing capability is shown as unavailable and is never simulated.